package com.yc.service;


import com.aliyun.oss.OSS;
import com.aliyun.oss.model.PutObjectRequest;
import com.yc.configure.OssConfig;
import com.yc.utils.FileTypeValidator;
import lombok.extern.java.Log;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.TimeUnit;

/**
 * 文件去重上传服务
 * 功能说明：
 *      - 校验上传文件类型是否合法
 *      - 计算文件的 MD5 值作为唯一标识
 *      - 通过 Redis 检查文件是否重复上传
 *      - 若是新文件，则上传到阿里云 OSS，并将 MD5 与文件 URL 存入 Redis
 *      - 若文件重复，则直接返回之前上传的 URL，避免重复存储
 *      使用 Redis 缓存 MD5 -> URL 的映射，并设置有效期为 30 天。
 */
@Service
@Log
public class FileDeduplicationService {

    private static final String REDIS_IMAGE_MD5_PREFIX = "image:md5:";// Redis 中存储文件 MD5 的 key 前缀
    private static final long REDIS_KEY_TTL_DAYS = 30; // Redis 缓存时间（单位：天）

    @Autowired
    private OSS ossClient;
    @Autowired
    private OssConfig ossConfig;
    @Autowired
    private StringRedisTemplate stringRedisTemplate; // Redis 操作类，用于读写字符串键值

    /**
     * 处理上传文件，若文件重复则返回已存在文件的 URL，否则上传到 OSS 并返回新 URL
      * @param file 要上传的文件
     * @return 文件访问 URL
     * @throws IOException 上传或读取过程中出错
     * @throws IllegalArgumentException 文件类型非法
     */
    public String processAndGetFileUrl(MultipartFile file) throws IOException {
        String contentType = file.getContentType();
        String originalFilename = file.getOriginalFilename();
        // 1. 校验文件类型   （通过 MIME + 扩展名）
        if (!FileTypeValidator.isValidFileType( contentType, originalFilename ) ) {
            throw new IllegalArgumentException("Invalid file type: " + originalFilename);
        }
        // 2. 计算文件的 MD5 值，作为文件唯一标识
        String md5Hash;
        try (InputStream is = file.getInputStream()) {
            md5Hash = DigestUtils.md5Hex(is);
        } catch (IOException e) {
            log.severe("Failed to calculate MD5 hash for file:"+ originalFilename);
            throw new IOException("Failed to process file: " + originalFilename, e);
        }
        // 3. 检查 Redis 中是否已有该文件的 MD5 记录
        String redisKey = REDIS_IMAGE_MD5_PREFIX + md5Hash;
        String existingFileUrl = stringRedisTemplate.opsForValue().get(redisKey);

        if (existingFileUrl != null && !existingFileUrl.isEmpty()) {  //文件已存在，直接返回旧的URL并刷新过期时间
            log.severe("Duplicate file detected for MD5: "+ md5Hash+". Returning existing URL: "+existingFileUrl);
            stringRedisTemplate.expire(redisKey, REDIS_KEY_TTL_DAYS, TimeUnit.DAYS); // 刷新过期时间
            return existingFileUrl;
        } else {   //4. 新文件，上传到 OSS
            log.info("New unique file detected for MD5: "+md5Hash+". Uploading to OSS.");
            // 构造上传路径：files
            String fileExtension = "";
            if (originalFilename != null && originalFilename.contains(".")) {
                fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
            }

            // String objectKey = "products/files/" + UUID.randomUUID().toString() + fileExtension; // e.g., products/files/uuid.jpg
            String objectKey = "files/" + md5Hash + fileExtension; // e.g., products/files/uuid.jpg
            // 上传到 OSS
             PutObjectRequest putObjectRequest = new PutObjectRequest(
                    ossConfig.getBucketName(),
                    objectKey,
                    file.getInputStream()
            );
            ossClient.putObject(putObjectRequest);
            // 拼接访问 URL（使用配置中的前缀 + 相对路径）
            String newFileUrl = ossConfig.getUrlPrefix()+ "/"  + objectKey;

            // 5. 把 MD5 -> URL 存入 Redis，避免重复上传
            stringRedisTemplate.opsForValue().set(redisKey, newFileUrl, REDIS_KEY_TTL_DAYS, TimeUnit.DAYS);
            log.info("File uploaded to OSS: "+ newFileUrl+" and MD5 stored in Redis.");
            return newFileUrl;
        }
    }
}

